﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace g4
{
    /// <summary>
    /// 网格整体挤出操作类
    /// 功能：对网格的所有面进行挤出，复制原始网格的三角形并偏移顶点位置，
    ///       同时通过三角形条带缝合原始网格与挤出网格的边界环，形成封闭结构
    /// 操作步骤：
    /// 1) 复制原始网格的所有三角形
    /// 2) 偏移复制得到的顶点位置
    /// 3) 用三角形条带连接原始网格与挤出网格的边界环
    /// </summary>
    public class MeshExtrudeMesh
    {
        /// <summary>
        /// 要进行挤出操作的原始网格
        /// </summary>
        public DMesh3 Mesh;

        // 操作参数

        /// <summary>
        /// 挤出面的分组行为（自动生成或指定组ID）
        /// </summary>
        public SetGroupBehavior OffsetGroup = SetGroupBehavior.AutoGenerate;

        /// <summary>
        /// 缝合三角形的分组行为（自动生成或指定组ID）
        /// </summary>
        public SetGroupBehavior StitchGroups = SetGroupBehavior.AutoGenerate;

        /// <summary>
        /// 计算挤出后顶点位置的函数
        /// 输入：原始顶点位置、原始顶点法向量、顶点ID
        /// 输出：挤出后新顶点的位置
        /// </summary>
        public Func<Vector3d, Vector3f, int, Vector3d> ExtrudedPositionF;

        /// <summary>
        /// 是否为正向偏移（与法向量同向）
        /// 若为负向偏移（如向内挤出），需设为false
        /// </summary>
        public bool IsPositiveOffset = true;

        // 操作结果

        /// <summary>
        /// 原始网格的初始边界环集合（可能为空，若原始网格是封闭的）
        /// </summary>
        public MeshBoundaryLoops InitialLoops;

        /// <summary>
        /// 原始网格的三角形ID数组
        /// </summary>
        public int[] InitialTriangles;

        /// <summary>
        /// 原始网格的顶点ID数组
        /// </summary>
        public int[] InitialVertices;

        /// <summary>
        /// 原始顶点到挤出顶点的映射（原始顶点ID -> 挤出顶点ID）
        /// </summary>
        public IndexMap InitialToOffsetMapV;

        /// <summary>
        /// 挤出面的三角形ID列表
        /// （注：挤出顶点可通过InitialToOffsetMapV映射原始顶点获得）
        /// </summary>
        List<int> OffsetTriangles;

        /// <summary>
        /// 挤出面所属的组ID
        /// </summary>
        public int OffsetGroupID;

        /// <summary>
        /// 挤出面上的新边界环数组（与InitialLoops一一对应）
        /// </summary>
        public EdgeLoop[] NewLoops;

        /// <summary>
        /// 连接每个边界环对的三角形条带数组
        /// （每个元素对应一个边界环的缝合三角形）
        /// </summary>
        public int[][] StitchTriangles;

        /// <summary>
        /// 每个缝合三角形条带所属的组ID数组
        /// </summary>
        public int[] StitchGroupIDs;


        /// <summary>
        /// 构造函数：初始化网格挤出操作
        /// </summary>
        /// <param name="mesh">要挤出的原始网格</param>
        public MeshExtrudeMesh(DMesh3 mesh)
        {
            Mesh = mesh;

            // 默认的挤出位置计算函数：沿顶点法向量方向偏移一个单位长度
            ExtrudedPositionF = (pos, normal, idx) => {
                return pos + normal; // 原始位置加上法向量（沿法向挤出）
            };
        }


        /// <summary>
        /// 验证操作的合法性
        /// </summary>
        /// <returns>验证状态（始终返回Ok，当前无特殊验证逻辑）</returns>
        public virtual ValidationStatus Validate()
        {
            // 目前没有需要验证的条件，理论上所有网格都可执行此操作
            return ValidationStatus.Ok;
        }


        /// <summary>
        /// 执行网格挤出操作的主方法
        /// </summary>
        /// <returns>操作是否成功</returns>
        public virtual bool Extrude()
        {
            MeshNormals normals = null;
            bool hasVertexNormals = Mesh.HasVertexNormals;
            // 若网格没有顶点法线，则临时计算
            if (!hasVertexNormals)
            {
                normals = new MeshNormals(Mesh);
                normals.Compute();
            }

            // 提取原始网格的边界环
            InitialLoops = new MeshBoundaryLoops(Mesh);
            // 记录原始网格的三角形和顶点ID
            InitialTriangles = Mesh.TriangleIndices().ToArray();
            InitialVertices = Mesh.VertexIndices().ToArray();

            // 复制原始网格的三角形，生成挤出面的基础
            InitialToOffsetMapV = new IndexMap(Mesh.MaxVertexID, Mesh.MaxVertexID);
            // 获取挤出面的组ID（根据OffsetGroup配置）
            OffsetGroupID = OffsetGroup.GetGroupID(Mesh);
            MeshEditor editor = new MeshEditor(Mesh);
            // 复制三角形并建立顶点映射，返回新三角形ID列表
            OffsetTriangles = editor.DuplicateTriangles(InitialTriangles, ref InitialToOffsetMapV, OffsetGroupID);

            // 计算并设置挤出顶点的位置
            foreach (int originalVid in InitialVertices)
            {
                int extrudedVid = InitialToOffsetMapV[originalVid]; // 映射到挤出顶点
                if (!Mesh.IsVertex(extrudedVid)) // 跳过无效顶点
                    continue;

                // 获取原始顶点位置和法线
                Vector3d originalPos = Mesh.GetVertex(originalVid);
                Vector3f normal = hasVertexNormals ?
                    Mesh.GetVertexNormal(originalVid) :
                    (Vector3f)normals.Normals[originalVid]; // 用临时计算的法线

                // 调用位置函数计算挤出后的位置
                Vector3d extrudedPos = ExtrudedPositionF(originalPos, normal, originalVid);
                // 更新挤出顶点的位置
                Mesh.SetVertex(extrudedVid, extrudedPos);
            }

            // 根据偏移方向反转三角形（确保法线方向正确）
            if (IsPositiveOffset)
            {
                // 正向偏移：反转原始三角形（避免与挤出面法线冲突）
                editor.ReverseTriangles(InitialTriangles);
            }
            else
            {
                // 负向偏移：反转挤出三角形
                editor.ReverseTriangles(OffsetTriangles);
            }

            // 缝合每个边界环（连接原始网格和挤出网格）
            NewLoops = new EdgeLoop[InitialLoops.Count];
            StitchTriangles = new int[InitialLoops.Count][];
            StitchGroupIDs = new int[InitialLoops.Count];
            int loopIndex = 0;

            foreach (var originalLoop in InitialLoops)
            {
                // 构建挤出面上对应的边界环（通过顶点映射）
                int[] extrudedLoopVertices = new int[originalLoop.VertexCount];
                for (int k = 0; k < extrudedLoopVertices.Length; ++k)
                {
                    extrudedLoopVertices[k] = InitialToOffsetMapV[originalLoop.Vertices[k]];
                }

                // 获取缝合三角形的组ID（根据StitchGroups配置）
                StitchGroupIDs[loopIndex] = StitchGroups.GetGroupID(Mesh);

                // 缝合原始环与挤出环，生成连接的三角形条带
                if (IsPositiveOffset)
                {
                    // 正向偏移：从挤出环向原始环缝合
                    StitchTriangles[loopIndex] = editor.StitchLoop(
                        extrudedLoopVertices, originalLoop.Vertices, StitchGroupIDs[loopIndex]);
                }
                else
                {
                    // 负向偏移：从原始环向挤出环缝合
                    StitchTriangles[loopIndex] = editor.StitchLoop(
                        originalLoop.Vertices, extrudedLoopVertices, StitchGroupIDs[loopIndex]);
                }

                // 记录挤出面上的新边界环
                NewLoops[loopIndex] = EdgeLoop.FromVertices(Mesh, extrudedLoopVertices);
                loopIndex++;
            }

            return true; // 挤出操作成功
        }
    }
}